문제
Description:
Add a groupBy method to Array.prototype so that elements in an array could be grouped by the result of evaluating a function on each element.
The method should return an object, in which for each different value returned by the function there is a property whose value is the array of elements that return the same value.
If no function is passed, the element itself should be taken.
Example:
[1,2,3,2,4,1,5,1,6].groupBy()
{
1: [1, 1, 1],
2: [2, 2],
3: [3],
4: [4],
5: [5],
6: [6]
}
[1,2,3,2,4,1,5,1,6].groupBy(function(val) { return val % 3;} )
{
0: [3, 6],
1: [1, 4, 1, 1],
2: [2, 2, 5]
}- 문제를 접했을 때 큰 생각이 없었다. 전략이 떠오르지 않으면 안되는데…
- 맨 처음에 prototype 안에서 배열에 어떻게 접근하는지 잘 몰라서 좀 고생했다. 알고 보니 this 객체로 접근할 수 있다더라…
My Solution
Array.prototype.groupBy = function(fn) {
var o = {};
this.forEach(function (data) {
var key;
if(fn) {
key = fn(data);
} else {
key = data;
}
if(o[key]) {
o[key].push(data);
} else {
o[key] = [data];
}
});
return o;
}- this 객체로 배열에 접근. fn이 있는지 없는지 찾은 다음에 key를 구한다. key를 가지고 리턴할 객체를 구성한다.
- 실제로 필드에서 사용할 때는 fn을 체크하는 로직 또한 들어가야 할 것 같다.
- 예를 들면 fn이 함수인지, 존재하는지, 함수이면 어떻게 동작하고 함수가 아닐 때는 어떻게 동작하는지 같은 로직을 추가해야 사용할 수 있을 거 같다.
Best Solution
Array.prototype.groupBy = function(fn) {
return this.reduce(function(o, a){
var v = fn ? fn(a) : a;
return (o[v] = o[v] || []).push(a), o;
}, {});
}- 왜 reduce를 사용할 생각을 못했지? 리듀스를 썼다면 코드가 훨씬 더 우아해 졌을 거 같다.
- 보통 베스트 솔루션들은 리듀스를 사용했다.
const _ = require('lodash');
Array.prototype.groupBy = function(fn){
return _.groupBy(this, fn);
}- 그렇군. 현명하다…